﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace Deflection
{
    class EnemyFactory
    {
        public enum EnemyFactoryState
        {

        }
        public enum ScreenSide
        {
            Top,
            Left,
            Bottom,
            Right,
            All
        }
        private static EnemyFactory instance = null;
        public int numSupers = 0;
        private int accumulatedMilliSeconds = 0;

        public Formation formation;
        private List<Enemy> enemies = new List<Enemy>();
        public List<Enemy> Enemies
        {
            get { return enemies; }
            set { enemies = value; }
        }

        //private CustomTimer timer = new CustomTimer();
        Random rand = new Random();
        public static EnemyFactory Instance()
        {
            if (instance == null)
            {
                instance = new EnemyFactory();
            }
            return instance;
        }

        // List for holding textures
        public List<Texture2D> Textures { get; set; }

        private EnemyFactory()
        {
            Textures = new List<Texture2D>();
        }

        public void Update(GameTime gameTime)
        {
            // Update all enemies
            for (int i = 0; i < enemies.Count; i++)
            {
                enemies[i].Update(gameTime);
            }

            // remove dead enemies
            enemies.RemoveAll(FindDeadEnemies);

            
        }
        public bool MakeSuperSquare()
        {
            int squareSuperSize = 5;//height or width
            int numberSquareEnemiesAvailableForSuper = enemies.Count(e => e.Type == Enemy.EnemyType.SquareEnemy && CheckEnemyForSuper(e));
            if (numberSquareEnemiesAvailableForSuper >= (squareSuperSize * squareSuperSize))
            {
                CreateEnemy(EnemyFactory.ScreenSide.Right, Enemy.EnemyType.SuperSquare);
                return true;
            }
            return false;
        }
        public bool MakeSuperTriangle()
        {
            int triangleSuperSize = 5;// base size
            int numberTriangleEnemiesAvailableForSuper = enemies.Count(e => e.Type == Enemy.EnemyType.TriangleEnemy && CheckEnemyForSuper(e));
            if (numberTriangleEnemiesAvailableForSuper >= (triangleSuperSize * triangleSuperSize))
            {
                CreateEnemy(EnemyFactory.ScreenSide.Right, Enemy.EnemyType.SuperTriangle);
                return true;
            }
            return false;
        }
        public bool MakeSuperHexagon()
        {
            int hexagonSuperSize = 5;//central column size
            int numberHexagonEnemiesAvailableForSuper = enemies.Count(e => e.Type == Enemy.EnemyType.HexagonEnemy && CheckEnemyForSuper(e));
            int sideColomnSize = (hexagonSuperSize + 1) / 2;

            int numOfHexagons = hexagonSuperSize;
            int counter = 0;

            // Get total number of hexagons
            for (int i = sideColomnSize; i < hexagonSuperSize; i++)
            {
                counter += i;
            }
            counter *= 2;

            numOfHexagons += counter;
            
            if (numberHexagonEnemiesAvailableForSuper >= numOfHexagons)
            {
                CreateEnemy(EnemyFactory.ScreenSide.Right, Enemy.EnemyType.SuperHexagon);
                return true;
            }
            return false;
        }
        private bool FindDeadEnemies(Enemy e)
        {
            if (e.State == Enemy.EnemyState.Dead)
            {
                if (e.Type == Enemy.EnemyType.SuperSquare || e.Type == Enemy.EnemyType.SuperTriangle || e.Type == Enemy.EnemyType.SuperHexagon)
                {
                    this.numSupers--;
                }
                return true;
            }
            return false;
        }

        public void FormationTest()
        {
            if (formation == null)
            {
                formation = new Formation(new Vector2(GameParameters.SCREEN_SIZE.X + 100, GameParameters.SCREEN_SIZE.Y / 2), Formations.Box);
                formation.scale = 0.25f;
                formation.CreateFormation(Enemy.EnemyType.SquareEnemy);
                // Add formation to subwave
                // formation.Update();

                Console.WriteLine("Formation");
            }
            else
            {
                formation.position = new Vector2(rand.Next(0, (int)GameParameters.SCREEN_SIZE.X), rand.Next(0, (int)GameParameters.SCREEN_SIZE.Y));

            }



        }

        public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
        {
            foreach (Enemy enemy in enemies)
            {
                enemy.Draw(gameTime, spriteBatch);
            }
        }

        public void LoadContent(ContentManager content)
        {

            Textures.Add(GameObject.LoadContent(content, "Sprites/EnemyDummy"));
            Textures.Add(GameObject.LoadContent(content, "Sprites/triangleEnemy"));
            Textures.Add(GameObject.LoadContent(content, "Sprites/hexEnemy"));
        }

        public void CreateEnemies()
        {

        }

        public Enemy CreateEnemy(ScreenSide sideToSpawnOn, Enemy.EnemyType enemyType)
        {
            Enemy enemy = null;
            Vector2 position = new Vector2();
            switch (sideToSpawnOn)
            {
                case ScreenSide.Top:
                    position = GameObject.GetRandomTopOfScreenSpawnPosition(rand);
                    break;
                case ScreenSide.Bottom:
                    position = GameObject.GetRandomBottomOfScreenSpawnPosition(rand);
                    break;
                case ScreenSide.Left:
                    position = GameObject.GetRandomLeftOfScreenSpawnPosition(rand);
                    break;
                case ScreenSide.Right:
                    position = GameObject.GetRandomRightOfScreenSpawnPosition(rand);
                    break;
                case ScreenSide.All:
                    int i = rand.Next(0, 5); //Switch case ?
                    if(i == 1)
                    {
                        position = GameObject.GetRandomTopOfScreenSpawnPosition(rand);
                    }
                    else if(i == 2)
                    {
                        position = GameObject.GetRandomBottomOfScreenSpawnPosition(rand);
                    }
                    else if(i == 3)
                    {
                        position = GameObject.GetRandomLeftOfScreenSpawnPosition(rand);
                    }
                    else if(i == 4)
                    {
                        position = GameObject.GetRandomRightOfScreenSpawnPosition(rand);
                    }
                    else
                    {
                        position = GameObject.GetRandomRightOfScreenSpawnPosition(rand);
                    }
                    break;
                default:
                    break;
            }

            switch (enemyType)
            {
                case Enemy.EnemyType.SquareEnemy:
                    enemy = new TestEnemy(position, Textures[(int)Enemy.EnemyType.SquareEnemy]);
                    break;
                case Enemy.EnemyType.TriangleEnemy:
                    enemy = new TriangleEnemy(position, Textures[(int)Enemy.EnemyType.TriangleEnemy]);
                    break;
                case Enemy.EnemyType.HexagonEnemy:
                    enemy = new HexagonEnemy(position, Textures[(int)Enemy.EnemyType.HexagonEnemy]);
                    break;
                case Enemy.EnemyType.SuperSquare:
                    List<Enemy> tempEnemies = new List<Enemy>();
                    int squareSuperSize = 5;
                    Enemy.EnemyType desiredType = Enemy.EnemyType.SquareEnemy;
                    // Find enemies to use in a super enemy
                    for (int i = 0; tempEnemies.Count < (squareSuperSize * squareSuperSize) && i < 75; i++)
                        {
                            // find a random enemy, and check it
                            Enemy randomEnemy = FindEnemyForSuper();
                            if (randomEnemy == null)
                            {
                                // cant find an enemy
                                break;
                            }
                            if (CheckEnemyForSuper(randomEnemy) && randomEnemy.Type == desiredType)
                            {
                                // Can be used as a super enemy
                                randomEnemy.partOfSuper = true;
                                randomEnemy.IsCollidable = false;
                                tempEnemies.Add(randomEnemy);
                            }

                        }
                    // Check if we have enough enemies to form the super enemy
                    if (tempEnemies.Count() == (squareSuperSize * squareSuperSize) && squareSuperSize != 0)
                    {
                        while (position.Y < (squareSuperSize * Textures[(int)Enemy.EnemyType.SquareEnemy].Bounds.Height) / 2)
                        {
                            position.Y += 2;
                        }
                        while (position.Y > GameParameters.SCREEN_SIZE.Y - (squareSuperSize * Textures[(int)Enemy.EnemyType.SquareEnemy].Bounds.Height) / 2)
                        {
                            position.Y -= 2;
                        }
                        enemy = new SuperEnemy(position, tempEnemies, new Vector2(squareSuperSize, squareSuperSize), Textures[0]);
                        numSupers++;
                    }
                    else
                    {
                        tempEnemies.ForEach(delegate(Enemy e)
                        {
                            e.partOfSuper = false;
                            e.IsCollidable = true;
                        });
                        return enemy;
                    }
                    
                    break;

                case Enemy.EnemyType.SuperTriangle:
                    List<Enemy> tempEnemiez = new List<Enemy>();
                    int triangleSuperSize = 5;
                    Enemy.EnemyType desiredtype = Enemy.EnemyType.TriangleEnemy;
                    // Find enemies to use in a super enemy
                    for (int i = 0; tempEnemiez.Count < (triangleSuperSize * triangleSuperSize) && i < 75; i++)
                    {
                        // find a random enemy, and check it
                        Enemy randomEnemy = FindEnemyForSuper();
                        if (randomEnemy == null)
                        {
                            // cant find an enemy
                            break;
                        }
                        if (CheckEnemyForSuper(randomEnemy) && randomEnemy.Type == desiredtype)
                        {
                            // Can be used as a super enemy
                            randomEnemy.partOfSuper = true;
                            randomEnemy.IsCollidable = false;
                            tempEnemiez.Add(randomEnemy);
                        }

                    }
                    // Check if we have enough enemies to form the super enemy
                    if (tempEnemiez.Count() == (triangleSuperSize * triangleSuperSize) && triangleSuperSize != 0)
                    {
                        while (position.Y < (triangleSuperSize * Textures[(int)Enemy.EnemyType.TriangleEnemy].Bounds.Height) / 2)
                        {
                            position.Y += 2;
                        }
                        while (position.Y > GameParameters.SCREEN_SIZE.Y - (triangleSuperSize * Textures[(int)Enemy.EnemyType.TriangleEnemy].Bounds.Height) / 2)
                        {
                            position.Y -= 2;
                        }
                        enemy = new SuperEnemy(position, tempEnemiez, new Vector2(triangleSuperSize, triangleSuperSize), Textures[1]);
                        numSupers++;
                    }
                    else
                    {
                        tempEnemiez.ForEach(delegate(Enemy e)
                        {
                            e.partOfSuper = false;
                            e.IsCollidable = true;
                        });
                        return enemy;
                    }

                    break;
                case Enemy.EnemyType.SuperHexagon:
                    List<Enemy> TempEnemies = new List<Enemy>();
                    int hexagonSuperSize = 5;
                    Enemy.EnemyType DesiredType = Enemy.EnemyType.HexagonEnemy;
                    
                    int sideColomnSize = (hexagonSuperSize + 1) / 2;

                    int numOfHexagons = hexagonSuperSize;
                    int counter = 0;

                    // Get total number of hexagons
                    for (int i = sideColomnSize; i < hexagonSuperSize; i++)
                    {
                        counter += i;
                    }
                    counter *= 2;

                    numOfHexagons += counter;
                    // Find enemies to use in a super enemy
                    for (int i = 0; TempEnemies.Count < numOfHexagons && i < 75; i++)
                    {
                        // find a random enemy, and check it
                        Enemy randomEnemy = FindEnemyForSuper();
                        if (randomEnemy == null)
                        {
                            // cant find an enemy
                            break;
                        }
                        if (CheckEnemyForSuper(randomEnemy) && randomEnemy.Type == DesiredType)
                        {
                            // Can be used as a super enemy
                            randomEnemy.partOfSuper = true;
                            randomEnemy.IsCollidable = false;
                            TempEnemies.Add(randomEnemy);
                        }

                    }
                    // Check if we have enough enemies to form the super enemy
                    if (TempEnemies.Count() == numOfHexagons && numOfHexagons != 0)
                    {

                        while (position.Y < (hexagonSuperSize * Textures[(int)Enemy.EnemyType.HexagonEnemy].Bounds.Height) / 2)
                        {
                            position.Y += 2;
                        }
                        while (position.Y > GameParameters.SCREEN_SIZE.Y - (hexagonSuperSize * Textures[(int)Enemy.EnemyType.HexagonEnemy].Bounds.Height))
                        {
                            position.Y -= 2;
                        }
                        enemy = new SuperEnemy(position, TempEnemies, new Vector2(hexagonSuperSize, hexagonSuperSize), Textures[2]);
                        numSupers++;
                    }
                    else
                    {
                        TempEnemies.ForEach(delegate(Enemy e)
                        {
                            e.partOfSuper = false;
                            e.IsCollidable = true;
                        });
                        return enemy;
                    }

                    break;
                default:
                    break;
            }
            // Adds enemy to the enemies list
            enemies.Add(enemy);
            return enemy;
        }

        /// <summary>
        /// Finds a random enemy from the collection of enemies
        /// </summary>
        /// <returns>A found enemy</returns>
        private Enemy FindEnemyForSuper()
        {
            // Check that we actually have some enemies
            if(enemies.Count > 0)
            {
                // pick a random enemy
                for (int i = 0; i < enemies.Count; i++)
                {
                    int e = rand.Next(0, enemies.Count());
                    if (CheckEnemyForSuper(enemies[e]))
                    {
                        return enemies[e];
                    }
                }
            }
            // We dont't have any enemies yet
            return null;
        }

        /// <summary>
        /// Finds a random enemy within a radius of a certain point
        /// </summary>
        /// <param name="origin">The center position of the circle</param>
        /// <param name="radius">The radius of the search area</param>
        /// <returns>A found enemy</returns>
        /*private Enemy FindEnemy(Vector2 origin, float radius)
        {

        }*/

        /// <summary>
        /// Checks to see if a specific enemy can be used to construct a super enemy
        /// </summary>
        /// <param name="enemy">The enenmy to check</param>
        /// <returns>True if it can be used</returns>
        private bool CheckEnemyForSuper(Enemy enemy)
        {
            if (!enemy.Alive)
            {
                return false;
            }
            if (enemy.partOfSuper)
            {
                return false;
            }
            if (enemy.Type == Enemy.EnemyType.SuperSquare)
            {
                return false;
            }
            if (enemy.State != Enemy.EnemyState.Alive)
            {
                return false;
            }
            if (enemy.position.X < GameParameters.SCREEN_SIZE.X / 10)
            {
                return false;
            }
            // Can be used as part of a super enemy
            return true;
        }

        public Enemy CreateEnemy(Vector2 position, Enemy.EnemyType enemyType)
        {
            Enemy enemy = null;
            switch (enemyType)
            {
                case Enemy.EnemyType.SquareEnemy:
                    enemy = new TestEnemy(position, Textures[(int)enemyType]);
                    break;
                default:
                    break;
            }
            // Adds enemy to the enemies list
            enemies.Add(enemy);
            return enemy;
        }

        public Enemy CreateEnemy(Vector2 position, Path path, Enemy.EnemyType enemyType)
        {
            Enemy enemy = null;
            switch (enemyType)
            {
                case Enemy.EnemyType.SquareEnemy:
                    enemy = new TestEnemy(position, Textures[(int)enemyType]);
                    break;
                default:
                    break;
            }
            // Adds enemy to the enemies list
            enemies.Add(enemy);
            return enemy;
        }
    }
}
